﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace CommonTools.UserControls.FlowChart
{

    /// <summary>
    /// LinkLineControl.xaml 的交互逻辑
    /// </summary>
    public partial class LinkLineControl : FlowBaseControl
    {


        public LinkLineModel Info
        {
            get { return (LinkLineModel)GetValue(InfoProperty); }
            set { SetValue(InfoProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Info.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty InfoProperty =
            DependencyProperty.Register("Info", typeof(LinkLineModel), typeof(LinkLineControl), new PropertyMetadata(null, (sender, e) =>
            {
                var dp = sender as LinkLineControl;
                if (dp == null) return;
                dp.GetInfo();
            }));



        public PointCollection PointList
        {
            get { return (PointCollection)GetValue(PointListProperty); }
            set { SetValue(PointListProperty, value); }
        }

        // Using a DependencyProperty as the backing store for PointList.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PointListProperty =
            DependencyProperty.Register("PointList", typeof(PointCollection), typeof(LinkLineControl), new PropertyMetadata(null));


        public double ScaleValue
        {
            get { return (double)GetValue(ScaleValueProperty); }
            set { SetValue(ScaleValueProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ScaleValue.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ScaleValueProperty =
            DependencyProperty.Register("ScaleValue", typeof(double), typeof(LinkLineControl), new PropertyMetadata(1.0));



        public Brush LineBrush
        {
            get { return (Brush)GetValue(LineBrushProperty); }
            set { SetValue(LineBrushProperty, value); }
        }

        // Using a DependencyProperty as the backing store for LineBrush.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty LineBrushProperty =
            DependencyProperty.Register("LineBrush", typeof(Brush), typeof(LinkLineControl), new PropertyMetadata(Brushes.Blue));



        public int ThumbSize
        {
            get { return (int)GetValue(ThumbSizeProperty); }
            set { SetValue(ThumbSizeProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ThumbSize.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ThumbSizeProperty =
            DependencyProperty.Register("ThumbSize", typeof(int), typeof(LinkLineControl), new PropertyMetadata(6));



        public ObservableCollection<Thumb> LineAdornerList
        {
            get { return (ObservableCollection<Thumb>)GetValue(LineAdornerListProperty); }
            set { SetValue(LineAdornerListProperty, value); }
        }

        // Using a DependencyProperty as the backing store for LineAdornerList.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty LineAdornerListProperty =
            DependencyProperty.Register("LineAdornerList", typeof(ObservableCollection<Thumb>), typeof(LinkLineControl), new PropertyMetadata(null));



        public int LineAccuracy
        {
            get { return (int)GetValue(LineAccuracyProperty); }
            set { SetValue(LineAccuracyProperty, value); }
        }

        // Using a DependencyProperty as the backing store for LineAccuracy.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty LineAccuracyProperty =
            DependencyProperty.Register("LineAccuracy", typeof(int), typeof(LinkLineControl), new PropertyMetadata(2));





        public int MaxInitTurnLength
        {
            get { return (int)GetValue(MaxInitTurnLengthProperty); }
            set { SetValue(MaxInitTurnLengthProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MaxInitTurnLength.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MaxInitTurnLengthProperty =
            DependencyProperty.Register("MaxInitTurnLength", typeof(int), typeof(LinkLineControl), new PropertyMetadata(20));




        public DirectionEnum StartDirection
        {
            get { return (DirectionEnum)GetValue(StartDirectionProperty); }
            set { SetValue(StartDirectionProperty, value); }
        }

        // Using a DependencyProperty as the backing store for StartDirection.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty StartDirectionProperty =
            DependencyProperty.Register("StartDirection", typeof(DirectionEnum), typeof(LinkLineControl), new PropertyMetadata(DirectionEnum.上));



        public DirectionEnum EndDirection
        {
            get { return (DirectionEnum)GetValue(EndDirectionProperty); }
            set { SetValue(EndDirectionProperty, value); }
        }

        // Using a DependencyProperty as the backing store for EndDirection.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty EndDirectionProperty =
            DependencyProperty.Register("EndDirection", typeof(DirectionEnum), typeof(LinkLineControl), new PropertyMetadata(DirectionEnum.下, (sender, e) =>
            {
                var dp = sender as LinkLineControl;
                if (dp == null) return;
                dp.EndDirectionChanged();
            }));



        public double ArrowX
        {
            get { return (double)GetValue(ArrowXProperty); }
            set { SetValue(ArrowXProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ArrowX.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ArrowXProperty =
            DependencyProperty.Register("ArrowX", typeof(double), typeof(LinkLineControl), new PropertyMetadata(0.0));



        public double ArrowY
        {
            get { return (double)GetValue(ArrowYProperty); }
            set { SetValue(ArrowYProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ArrowY.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ArrowYProperty =
            DependencyProperty.Register("ArrowY", typeof(double), typeof(LinkLineControl), new PropertyMetadata(0.0));




        public int ArrowAngle
        {
            get { return (int)GetValue(ArrowAngleProperty); }
            set { SetValue(ArrowAngleProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ArrowAngle.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ArrowAngleProperty =
            DependencyProperty.Register("ArrowAngle", typeof(int), typeof(LinkLineControl), new PropertyMetadata(0));




        private LineDirectionEnum direction = LineDirectionEnum.左右;
        private bool middleMove = false;

        private void GetInfo()
        {
            if (Info == null) return;
            LineAdornerList = new ObservableCollection<Thumb>();
        }

        public LinkLineControl()
        {
            InitializeComponent();
        }

        private void EndDirectionChanged()
        {
            switch (EndDirection)
            {
                case DirectionEnum.上:
                    ArrowAngle = 0;
                    break;
                case DirectionEnum.右:
                    ArrowAngle = 90;
                    break;
                case DirectionEnum.下:
                    ArrowAngle = 180;
                    break;
                case DirectionEnum.左:
                    ArrowAngle = 270;
                    break;
            }
        }


        public void GetLastPoint(Point point)
        {
            if (Info.PointList.Count <= 1)
                Info.PointList.Add(new Point_Link(point.X, point.Y));
            else
            {
                var last = Info.PointList.LastOrDefault();
                if (last != null)
                {
                    last.X = point.X;
                    last.Y = point.Y;
                }
            }
            CreatePoint();
        }

        private void CreatePoint()
        {
            if (Info.PointList.Count < 2) return;
            direction = CalcDirection();
            var first = Info.PointList.FirstOrDefault();
            var last = Info.PointList.LastOrDefault();
            if (first == null || last == null) return;
            List<Point_Link> points = new List<Point_Link>() { Info.PointList.FirstOrDefault(), Info.PointList.LastOrDefault() };
            switch (direction)
            {
                case LineDirectionEnum.左右:
                    EndDirection = DirectionEnum.右;
                    break;
                case LineDirectionEnum.右左:
                    EndDirection = DirectionEnum.左;
                    break;
                case LineDirectionEnum.上下:
                    EndDirection = DirectionEnum.上;
                    break;
                case LineDirectionEnum.下上:
                    EndDirection = DirectionEnum.上;
                    break;
                case LineDirectionEnum.左上右下:
                case LineDirectionEnum.左下右上:
                    if (Math.Abs(last.X - first.X) > MaxInitTurnLength)
                    {
                        points.Insert(1, new Point_Link(first.X + MaxInitTurnLength, first.Y));
                        points.Insert(2, new Point_Link(first.X + MaxInitTurnLength, last.Y));
                    }
                    else
                    {
                        points.Insert(1, new Point_Link((first.X + last.X) / 2, first.Y));
                        points.Insert(2, new Point_Link((first.X + last.X) / 2, last.Y));
                    }
                    EndDirection = DirectionEnum.右;
                    break;
                case LineDirectionEnum.右下左上:
                case LineDirectionEnum.右上左下:
                    if (Math.Abs(last.X - first.X) > MaxInitTurnLength)
                    {
                        points.Insert(1, new Point_Link(first.X - MaxInitTurnLength, first.Y));
                        points.Insert(2, new Point_Link(first.X - MaxInitTurnLength, last.Y));
                    }
                    else
                    {
                        points.Insert(1, new Point_Link((first.X + last.X) / 2, first.Y));
                        points.Insert(2, new Point_Link((first.X + last.X) / 2, last.Y));
                    }
                    EndDirection = DirectionEnum.左;
                    break;

            }
            points.ForEach(x => x.Order = points.IndexOf(x));
            Info.PointList = new ObservableCollection<Point_Link>(points);

            RefushPolyLine();
            RefushArrow();
            RefushPointList();
        }

        private void RefushArrow()
        {
            ArrowX = Info.PointList.LastOrDefault().X;
            ArrowY = Info.PointList.LastOrDefault().Y;
        }


        private LineDirectionEnum CalcDirection()
        {
            if (Info == null || Info.PointList.Count < 2) return LineDirectionEnum.上下;
            var start = Info.PointList.FirstOrDefault();
            var end = Info.PointList.LastOrDefault();
            var dx = (end.X - start.X);
            var dy = (end.Y - start.Y);
            if (Math.Abs(dx) < 1)
                return dy > 0 ? LineDirectionEnum.上下 : LineDirectionEnum.下上;
            else if (Math.Abs(dy) < 1)
                return dx > 0 ? LineDirectionEnum.左右 : LineDirectionEnum.右左;
            else if (dx < 0 && dy < 0)
                return LineDirectionEnum.右下左上;
            else if (dx < 0 && dy > 0)
                return LineDirectionEnum.右上左下;
            else if (dx > 0 && dy < 0)
                return LineDirectionEnum.左下右上;
            else
                return LineDirectionEnum.左上右下;
        }

        private void RefushPointList()
        {
            if (LineAdornerList == null) return;
            LineAdornerList = new ObservableCollection<Thumb>();
            foreach (var item in Info.PointList)
            {
                Thumb thumb = new Thumb() { Tag = item.Order };
                thumb.HorizontalAlignment = HorizontalAlignment.Stretch;
                thumb.VerticalAlignment = VerticalAlignment.Stretch;
                thumb.Cursor = Cursors.Cross;
                thumb.DragDelta += Thumb_DragDelta;
                thumb.MouseLeftButtonUp += Thumb_MouseLeftButtonUp;
                thumb.Background = Brushes.Green;
                thumb.Opacity = 1;
                thumb.Template = new ControlTemplate(typeof(Thumb))
                {
                    VisualTree = GetFactory(LineBrush)
                };
                Binding widthBind = new Binding();
                widthBind.Source = this;
                widthBind.Path = new PropertyPath("ScaleValue");
                widthBind.Mode = BindingMode.TwoWay;
                widthBind.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
                widthBind.Converter = new NumDivideConvert();
                widthBind.ConverterParameter = ThumbSize;
                thumb.SetBinding(Thumb.WidthProperty, widthBind);
                Binding heightBind = new Binding();
                heightBind.Source = this;
                heightBind.Path = new PropertyPath("ScaleValue");
                heightBind.Mode = BindingMode.TwoWay;
                heightBind.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
                heightBind.Converter = new NumDivideConvert();
                heightBind.ConverterParameter = ThumbSize;
                thumb.SetBinding(Thumb.HeightProperty, heightBind);

                LineAdornerList.Add(thumb);
                Canvas.SetLeft(thumb, item.X - ThumbSize / 2 / ScaleValue);
                Canvas.SetTop(thumb, item.Y - ThumbSize / 2 / ScaleValue);
            }
        }

        private void RefushPolyLine()
        {
            if (PointList == null) PointList = new PointCollection();
            else PointList.Clear();
            foreach (var item in Info.PointList)
            {
                PointList.Add(new Point(item.X, item.Y));
            }
        }
        private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            var thumb = sender as Thumb;
            if (thumb == null) return;
            if (int.TryParse(thumb.Tag.ToString(), out int index))
            {
                if (index >= Info.PointList.Count) return;
                Info.PointList[index] = new Point_Link((int)(Info.PointList[index].X + e.HorizontalChange) / LineAccuracy * LineAccuracy, 
                    (int)(Info.PointList[index].Y + e.VerticalChange) / LineAccuracy * LineAccuracy);
                switch (index)
                {
                    case 0:
                        PointChanged(1, (int)Info.PointList[1].X / LineAccuracy * LineAccuracy,
                            (int)(Info.PointList[1].Y + e.VerticalChange) / LineAccuracy * LineAccuracy);
                        break;
                    case 1:
                        switch (direction)
                        {
                            case LineDirectionEnum.右上左下:
                            case LineDirectionEnum.右下左上:
                            case LineDirectionEnum.左上右下:
                            case LineDirectionEnum.左下右上:
                                PointChanged(0, (int)Info.PointList[0].X / LineAccuracy * LineAccuracy, 
                                    (int)(Info.PointList[0].Y + e.VerticalChange) / LineAccuracy * LineAccuracy);
                                PointChanged(2, (int)(Info.PointList[2].X + e.HorizontalChange) / LineAccuracy * LineAccuracy,
                                    (int)Info.PointList[2].Y / LineAccuracy * LineAccuracy);
                                break;
                            default:
                                CreatePoint();
                                break;
               
                        }
                        break;
                    case 2:
                        switch (direction)
                        {
                            case LineDirectionEnum.右上左下:
                            case LineDirectionEnum.右下左上:
                            case LineDirectionEnum.左上右下:
                            case LineDirectionEnum.左下右上:
                                PointChanged(1, (int)(Info.PointList[1].X + e.HorizontalChange) / LineAccuracy * LineAccuracy,
                                    (int)Info.PointList[1].Y / LineAccuracy * LineAccuracy);
                                PointChanged(3, (int)Info.PointList[3].X / LineAccuracy * LineAccuracy,
                                    (int)(Info.PointList[3].Y + e.VerticalChange) / LineAccuracy * LineAccuracy);
                                break;

                        }
                        break;
                    case 3:
                        PointChanged(2, (int)Info.PointList[2].X / LineAccuracy * LineAccuracy,
                            (int)(Info.PointList[2].Y + e.VerticalChange) / LineAccuracy * LineAccuracy);
                        break;
                }
                Canvas.SetLeft(thumb, Info.PointList[index].X - ThumbSize / 2 / ScaleValue);
                Canvas.SetTop(thumb, Info.PointList[index].Y - ThumbSize / 2 / ScaleValue);
                //direction = CalcDirection();
                //if ((int)direction < 4 && Info.PointList.Count == 4)
                //{
                //    Info.PointList.RemoveAt(1);
                //    Info.PointList.RemoveAt(1);
                //    Info.PointList.LastOrDefault().Order = 1;
                //    LineAdornerList.RemoveAt(1);
                //    LineAdornerList.RemoveAt(1);
                //    LineAdornerList.LastOrDefault().Tag = 1;
                //}
                RefushPolyLine();
                RefushArrow();
    
            }
        }

        private void PointChanged(int index, int x, int y)
        {
            Info.PointList[index] = new Point_Link(x, y);
            Canvas.SetLeft(LineAdornerList[index], x - ThumbSize / 2 / ScaleValue);
            Canvas.SetTop(LineAdornerList[index], y - ThumbSize / 2 / ScaleValue);
        }

        private void Thumb_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            Canvas parent = VisualTreeHelper.GetParent(VisualTreeHelper.GetParent(VisualTreeHelper.GetParent(VisualTreeHelper.GetParent(VisualTreeHelper.GetParent(VisualTreeHelper.GetParent(this)))))) as Canvas;
            if (parent != null)
            {
                MouseButtonEventArgs args = new MouseButtonEventArgs(Mouse.PrimaryDevice, Environment.TickCount, MouseButton.Left);
                args.RoutedEvent = UIElement.MouseLeftButtonUpEvent;

                parent.RaiseEvent(args);
            }
        }

        private FrameworkElementFactory GetFactory(Brush back)
        {
            var fef = new FrameworkElementFactory(typeof(Ellipse));
            fef.SetValue(Ellipse.FillProperty, back);
            return fef;
        }

        
    }
}
